package com.asurplus.common.utils;

import cn.hutool.core.collection.CollectionUtil;
import com.asurplus.common.vo.NodeProperties;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 数据源工具类
 *
 * @author asurplus
 */
@Slf4j
public class DataSourceUtil {

    private static final List<String> FILTER_DATABASE = Arrays.asList("information_schema", "mysql", "performance_schema", "sys");

    /**
     * 拼接url
     *
     * @param host
     * @param port
     * @return
     */
    public static String getUrl(String host, String port, String database) {
        if (StringUtils.isBlank(database)) {
            database = "mysql";
        }
        return "jdbc:mysql://" + host + ":" + port + "/" + database + "?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8";
    }

    /**
     * 数据库连接配置
     *
     * @param properties 数据库连接信息
     * @return
     */
    public static HikariConfig getHikariConfig(NodeProperties properties) {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setDriverClassName(properties.getClassName());
        hikariConfig.setJdbcUrl(getUrl(properties.getHost(), properties.getPort(), properties.getDatabase()));

        hikariConfig.setUsername(properties.getUsername());
        hikariConfig.setPassword(properties.getPassword());

        hikariConfig.setMaximumPoolSize(2);
        hikariConfig.setMinimumIdle(1);
        hikariConfig.setAutoCommit(true);
        hikariConfig.setConnectionTimeout(6000);
        hikariConfig.setConnectionTestQuery("SELECT 1 FROM DUAL");
        hikariConfig.addDataSourceProperty("cachePrepStmts", "true");
        hikariConfig.addDataSourceProperty("prepStmtCacheSize", "250");
        hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        return hikariConfig;
    }


    /**
     * 自定义连接数据源
     *
     * @param hikariConfig
     * @return
     */
    public static HikariDataSource createDataSource(HikariConfig hikariConfig) {
        if (null == hikariConfig) {
            return null;
        }
        return new HikariDataSource(hikariConfig);
    }

    /**
     * 自定义连接数据源
     *
     * @param properties
     * @return
     */
    public static HikariDataSource createDataSource(NodeProperties properties) {
        return createDataSource(getHikariConfig(properties));
    }

    /**
     * 获取数据库信息
     */
    public static List<String> listDataBases(DataSource dataSource) {
        try {
            JdbcTemplate template = new JdbcTemplate(dataSource);
            List<String> databases = template.query("SHOW DATABASES", new RowMapper<String>() {
                public String mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return rs.getString(1);
                }
            }, null);
            return filterDatabase(databases);
        } catch (Exception e) {
            log.error("获取数据库信息失败：{}", e.getMessage());
        }
        return null;
    }

    /**
     * 获取数据表信息
     */
    public static List<String> listTables(DataSource dataSource) {
        try {
            JdbcTemplate template = new JdbcTemplate(dataSource);
            return template.query("SHOW TABLES;", new RowMapper<String>() {
                public String mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return rs.getString(1);
                }
            }, null);
        } catch (Exception e) {
            log.error("获取数据表信息失败：{}", e.getMessage());
        }
        return null;
    }

    /**
     * 过滤MySQL默认数据库
     */
    public static List<String> filterDatabase(List<String> list) {
        if (CollectionUtil.isEmpty(list)) {
            return null;
        }
        List<String> resList = new ArrayList<>();
        for (String item : list) {
            if (!FILTER_DATABASE.contains(item)) {
                resList.add(item);
            }
        }
        return resList;
    }
}
